Skip to content

fix: give each escrow/subscription row an independent pending indicator - #65

Merged
abayomicornelius merged 10 commits into
StellarSend:mainfrom
presidojay1:fix/per-row-pending-state-52
Aug 17, 2026
Merged

abayomicornelius merged 10 commits into
StellarSend:mainfrom
presidojay1:fix/per-row-pending-state-52

Conversation

@presidojay1

Copy link
Copy Markdown
Contributor

Summary

Escrow.tsx and Subscriptions.tsx each derived every row's loading spinner from a single shared mutation's .variables — the argument of the most recent .mutate() call, not a record of everything currently pending. Releasing escrow B while escrow A's release was still in flight silently overwrote releaseMutation.variables to 'escrowB', so escrow A's spinner disappeared and its button re-enabled even though its request hadn't settled — inviting a second, racing call against the same escrow. Identical shape for refundMutation and Subscriptions.tsx's cancelMutation.

  • New usePendingIds hook tracks a Set of in-flight ids instead, updated via track(id, promise) independently of any single mutation instance.
  • Escrow.tsx/Subscriptions.tsx now call mutation.mutateAsync(id) through track rather than deriving state from .variables.
  • EscrowList/SubscriptionList derive each row's isReleasing/isRefunding/isCancelling from Set.has(id) instead of scalar equality.

Closes #52

Test plan

  • npm run type-check — clean
  • npm run lint — clean
  • npm run build — succeeds
  • npx vitest run — 91/91 pass (74 pre-existing + 17 new), including:

A single shared useMutation's .variables is a scalar overwritten by
the most recent .mutate() call, not a record of everything currently
pending — so deriving a row's loading indicator from it silently
clobbers another row's indicator when two actions overlap (StellarSend#52).
usePendingIds tracks a Set of in-flight ids instead, updated
independently of any single mutation instance.
releaseMutation.variables is a single scalar overwritten by the most
recent .mutate() call — releasing escrow B while escrow A's release
is still in flight silently un-showed A's spinner (and re-enabled its
button) even though A's request hadn't settled (StellarSend#52). Escrow.tsx now
tracks releasing ids via usePendingIds, and EscrowList/EscrowItem
derive isReleasing from Set membership instead of scalar equality.
Same clobbering bug as the release fix, but for refundMutation — a
depositor refunding escrow B while escrow A's refund is still in
flight silently un-showed A's spinner (StellarSend#52). EscrowList now derives
isRefunding from a Set of in-flight refunding ids too, matching the
release fix.
…ator

Identical shape to the escrow release/refund bug: cancelMutation was
shared across the whole SubscriptionList, so cancelling subscription
B while subscription A's cancellation was still in flight silently
un-showed A's spinner (StellarSend#52). Subscriptions.tsx now tracks cancelling
ids via usePendingIds, matching the escrow fix.
Two ids tracked concurrently stay independently pending; resolving
one never clears a different, still-pending id — the exact clobbering
this hook exists to prevent (StellarSend#52). Also covers rejection (cleared
without an unhandled rejection), same-id re-tracking, and track's
referential stability across renders.
Two different rows in releasingIds show as releasing at the same
time without either one's state overwriting the other's — the UI
symptom of StellarSend#52. Also covers undefined releasingIds rendering nothing
as pending.
Mirrors the EscrowList coverage: two different rows in cancellingIds
show as cancelling at the same time without either one's state
overwriting the other's (StellarSend#52).
Combines the real useReleaseEscrow mutation with usePendingIds
(mocking only the API/wallet boundary), following the issue's own
suggested testing strategy: releasing escrow B while escrow A's
release is still in flight must not affect A's pending state.
Explicitly asserts releaseMutation.variables becomes 'escrowB' at
that point — the old, wrong source of truth — while pendingIds
correctly still has both.
Mirrors the release test for useRefundEscrow: refunding escrow B
while escrow A's refund is still in flight must not affect A's
pending state, proven against the real mutation rather than a mock
of usePendingIds itself.
Same shape as the escrow release/refund tests but for
useCancelSubscription: cancelling subscription B while A's
cancellation is still in flight must not affect A's pending state.
Completes coverage for all three mutations named in StellarSend#52.
@abayomicornelius

Copy link
Copy Markdown
Contributor

All 4 checks passed. good one

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants